package com.tcm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tcm.config.AppProperties;
import com.tcm.entity.*;
import com.tcm.entity.dto.param.ImageUpdateParam;
import com.tcm.entity.dto.ImageUploadDto;
import com.tcm.entity.vo.ImageListResult;
import com.tcm.entity.resp.PageR;
import com.tcm.exception.DatabaseOperationException;
import com.tcm.exception.EntityInvalidException;
import com.tcm.mapper.ImageMapper;
import com.tcm.mapper.ItemImageMapper;
import com.tcm.service.BookService;
import com.tcm.service.CodeService;
import com.tcm.service.ImageService;
import com.tcm.service.UserService;
import com.tcm.utils.FastDFSUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * @author Guqier
 * @version 1.0
 * @date 2023/7/2 17:13
 */
@Slf4j
@Service
public class ImageServiceImpl extends ServiceImpl<ImageMapper, Image> implements ImageService {

    @Autowired
    private CodeService codeService;

    @Autowired
    private BookService bookService;

    @Autowired
    private UserService userService;

    @Autowired
    private ItemImageMapper itemImageMapper;


    private static final String IMAGE_CODE_TYPE = "imageType";// 课程封面类型

    public static final int COURSE_TYPE = 0;


    /**
     * 图片批量上传
     * @param imageUploadDtos 批量上传图片数据Dto
     * @param currUserId 当前登录用户ID
     * @return 图片上传后回执信息
     */
    @Override
    @Transactional
    public List<Image> uploadBatch(List<ImageUploadDto> imageUploadDtos, Long currUserId){
        List<Image> images = new ArrayList<>();
        for (ImageUploadDto imageUploadDto : imageUploadDtos) {
            String[] res = null;
            try {
                res = FastDFSUtils.upload(imageUploadDto.getFile());
            } catch (Exception e) {
                log.error("文件上传失败：{}", e.getMessage());
                break;
            }

            // 保存数据
            Image image = new Image();
            image.setName(imageUploadDto.getName());
            image.setType(imageUploadDto.getType());
            image.setSequence(imageUploadDto.getSequence() != null ? imageUploadDto.getSequence() : 1);
            image.setGroupName(res[0]);
            image.setOldFilename(res[1]);
            image.setRemoteFileUrl(FastDFSUtils.getTrackerUrl()+ res[0] + "/" + res[1]);
            image.setCreateUser(currUserId);
            image.setUpdateUser(currUserId);
            boolean save = this.save(image);
            if (save) {
                images.add(image);
            }
        }

        // 回滚已上传
        if (images.size() != imageUploadDtos.size()){
            if (!CollectionUtils.isEmpty(images)) {
                for (Image image : images) {
                    try {
                        FastDFSUtils.deleteFile(image.getGroupName(), image.getOldFilename());
                    } catch (Exception ignored) {
                    }
                }
            }
            throw new DatabaseOperationException("图片上传失败");
        }
        return images;
    }

    /**
     * 分页获取图片列表
     * @param pageNum 当前页码
     * @param pageSize 页面大小
     * @param type 图片类型
     * @param name 查询名称
     * @param imageIds 可选，指定图片ID提到最前
     * @return 分页数据
     */
    @Override
    public PageR<ImageListResult> getImageListByPage(Integer pageNum, Integer pageSize, Integer type, String name, List<Long> imageIds){
        Page<Image> page = new Page<>(pageNum, pageSize);
        page.addOrder(OrderItem.desc("update_time"));
        LambdaQueryWrapper<Image> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(type != null, Image::getType, type);
        queryWrapper.like(name != null, Image::getName, name);

        this.page(page, queryWrapper);// 查询数据封装在page中
        PageR<ImageListResult> pageRList = new PageR<>();
        // 拼接响应数据
        BeanUtils.copyProperties(page, pageRList, "records");
        List<Image> records = page.getRecords();
        List<ImageListResult> res = new ArrayList<>();
        // 将指定image提前
        if (!CollectionUtils.isEmpty(imageIds)){
            // 获取指定image
            List<Image> images = this.listByIds(imageIds);
            // 从原有列表中移除
            records.removeAll(images);
            // 将指定Image添加到首位
            records.addAll(0, images);
        }
        // 获取所有图片类型
        LambdaQueryWrapper<Code> queryWrapper1 = new LambdaQueryWrapper<>();
        queryWrapper1.eq(Code::getType, IMAGE_CODE_TYPE);
        List<Code> imageType = codeService.list(queryWrapper1);
        for (Image record : records) {
            ImageListResult imageListResult = new ImageListResult();
            BeanUtils.copyProperties(record, imageListResult, "type");
            imageListResult.setType(record.getType());
            imageType.stream().filter(t -> t.getCode().equals(record.getType().toString())).findFirst().ifPresent(code -> imageListResult.setTypeName(code.getName()));
            res.add(imageListResult);
        }
        pageRList.setRecords(res);
        return pageRList;
    }

    /**
     * 修改图片信息
     * @param imageInfo 图片更新信息封装对象
     * @param currUserId 当前登录用户ID
     * @return 更新结果，true为成功
     */
    @Override
    @Transactional
    @CacheEvict(value = AppProperties.ENTITY_CACHE_IMAGE, key = "#imageInfo.id", condition = "#imageInfo.id != null")
    public boolean modifyImageInfo(ImageUpdateParam imageInfo, Long currUserId) throws EntityInvalidException{
        // 判断图片是否存在
        Image image = this.getById(imageInfo.getId());
        if (image == null) {
            throw new EntityInvalidException("图片不存在");
        }
        // 填充修改信息
        image.setName(imageInfo.getName());
        image.setType(imageInfo.getType());
        image.setSequence(imageInfo.getSequence() != null ? imageInfo.getSequence() : image.getSequence());
        image.setUpdateUser(currUserId);

        // 更新到数据库
        return this.updateById(image);
    }

    /**
     * 批量删除图片
     * @param ids 图片集合
     * @param currUserId 当前登录用户ID
     * @return 删除结果，true为成功
     * @throws DatabaseOperationException 数据库操作异常
     */
    @Override
    @Transactional
    public boolean removeImage(List<Long> ids, Long currUserId) throws Exception {
        // 获取课程列表
        LambdaQueryWrapper<Book> bookLambdaQueryWrapper = new LambdaQueryWrapper<>();
        bookLambdaQueryWrapper.eq(Book::getType, COURSE_TYPE);
        List<Book> courses = bookService.list(bookLambdaQueryWrapper);
        // 获取用户头像不为空的用户列表
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userLambdaQueryWrapper.isNotNull(User::getAvatar);
        List<User> users = userService.list(userLambdaQueryWrapper);
        // 获取题目图片关系列表
        List<ItemImage> itemImages = itemImageMapper.selectList(new LambdaQueryWrapper<ItemImage>().in(ItemImage::getImageId, ids));

        int cnt = 0;
        for (Long id : ids) {
            Image image = this.getById(id);
            if (image == null) {
                continue;
            }
            /* 校验图片引用 */
            // 校验课程封面引用
            Optional<Book> book = courses.stream().filter(course -> course.getImageId().equals(id)).findFirst();
            if (book.isPresent()){
                throw new DatabaseOperationException("图片【" + image.getName() +"】与课程【" + book.get().getName() + "】关联，删除失败");
            }
            // 校验用户头像引用
            Optional<User> avatar = users.stream().filter(user -> user.getAvatar().equals(id)).findFirst();
            if (avatar.isPresent()){
                throw new DatabaseOperationException("图片【" + image.getName() +"】与用户【" + avatar.get().getName() + "】关联，删除失败");
            }
            // 校验题目配图引用
            Optional<ItemImage> item = itemImages.stream().filter(itemImage -> itemImage.getImageId().equals(id)).findFirst();
            if (item.isPresent()){
                throw new DatabaseOperationException("图片【" + image.getName() +"】与题目【" + item.get().getItemId() + "】关联，删除失败");
            }

            image.setUpdateUser(currUserId);
            this.updateById(image);
            FastDFSUtils.deleteFile(image.getGroupName(), image.getOldFilename());
            if (this.removeImageById(id)) {
                cnt++;
            }
        }
        if (cnt != ids.size()) {
            throw new DatabaseOperationException("图片ID不正确");
        }
        return true;
    }

    /**
     * 上传单个图片
     * @param imageUploadDto 图片数据封装Dto
     * @param currUserId 当前用户ID
     * @return 上传结果，true为成功
     * @throws Exception 异常
     */
    @Override
    @Transactional
    public Map<String, String> uploadSingle(ImageUploadDto imageUploadDto, Long currUserId) throws Exception {
        String[] res = null;
        try {
            res = FastDFSUtils.upload(imageUploadDto.getFile());
        } catch (Exception e) {
            throw new Exception("图片上传失败");
        }

        // 保存数据
        Image image = new Image();
        image.setName(imageUploadDto.getName());
        image.setType(imageUploadDto.getType());
        image.setSequence(imageUploadDto.getSequence() != null ? imageUploadDto.getSequence() : 1);
        image.setGroupName(res[0]);
        image.setOldFilename(res[1]);
        image.setRemoteFileUrl(FastDFSUtils.getTrackerUrl()+ res[0] + "/" + res[1]);
        image.setCreateUser(currUserId);
        image.setUpdateUser(currUserId);
        boolean save = this.save(image);
        if (!save){
            FastDFSUtils.deleteFile(res[0], res[1]);
            throw new Exception("图片信息保存失败");
        }
        Map<String, String> map = new HashMap<>();
        map.put("imageId", image.getId().toString());
        map.put("imageUrl", image.getRemoteFileUrl());
        return map;
    }

    /**
     * 获取题目图片
     * @param itemId 题目ID
     * @return 图片列表
     */
    @Override
    public List<Image> getImagesByItemId(Long itemId){
        return this.getBaseMapper().getImagesByItemId(itemId);
    }

    /**
     * 根据ID获取图片对象（缓存）
     * @param id 图片ID
     * @return 图片对象
     */
    @Override
    @Cacheable(value = AppProperties.ENTITY_CACHE_IMAGE, key = "#id", unless = "#result == null")
    public Image getImageById(Long id){
        return this.getById(id);
    }

    /**
     * 根据ID删除图片（缓存）
     * @param id 图片ID
     * @return 删除结果
     */
    @Override
    @CacheEvict(value = AppProperties.ENTITY_CACHE_IMAGE, key = "#id", condition = "#id != null")
    public boolean removeImageById(Long id){
        return this.removeById(id);
    }

}
